#include <cassert>
#include "Window.h"

Window::Window()
{

}

Window::~Window()
{

}

LRESULT CALLBACK Window::WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    int wmId, wmEvent;

    switch (message)
    {
    case WM_CREATE:
        //window_handle = hWnd;
        OnCreate(hWnd, (LPCREATESTRUCT)lParam);
        break;
    case WM_COMMAND:
        wmId    = LOWORD(wParam);
        wmEvent = HIWORD(wParam);
        OnCommand(wmId, wmEvent, lParam);
        break;
    case WM_PAINT:
        OnPaint();
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_NOTIFY:
        OnNotify((int)wParam, (LPNMHDR)lParam);
        break;
    case WM_KEYDOWN:
        OnKeyDown(static_cast<UINT>(wParam), 
            static_cast<UINT>(LOWORD(lParam)),
            static_cast<UINT>(HIWORD(lParam)));
        break;
    case WM_KEYUP:
        OnKeyUp(static_cast<UINT>(wParam), 
            static_cast<UINT>(LOWORD(lParam)),
            static_cast<UINT>(HIWORD(lParam)));
        break;
    case WM_LBUTTONDOWN: 
        {
            Point point = {LOWORD(lParam), HIWORD(lParam)};
            OnLButtonDown(static_cast<UINT>(wParam), point);
        }
        break;
    case WM_MOUSEMOVE: 
        {
            Point point = {LOWORD(lParam), HIWORD(lParam)};
            OnMouseMove(static_cast<UINT>(wParam), point);
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

BOOL Window::CreateEx( DWORD dwExStyle, LPCTSTR lpszClass, LPCTSTR lpszName, 
                      DWORD dwStyle, int x, int y, int nWidth, int nHeight, 
                      HWND hParent, HMENU hMenu, HINSTANCE hInst )
{
    if ( ! RegisterClass(lpszClass, hInst) )
        return FALSE;
    window_handle = CreateWindowEx(dwExStyle, lpszClass, lpszName, dwStyle, 
        x, y, nWidth, nHeight, hParent, hMenu, hInst, this);

    return window_handle != NULL;
}

BOOL Window::RegisterClass( LPCTSTR lpszClass, HINSTANCE hInst )
{
    WNDCLASSEX wcex;

    wcex.cbSize = sizeof(wcex);

    if (!GetClassInfoEx(hInst, lpszClass, &wcex)){
        GetWndClassEx(wcex);

        wcex.hInstance     = hInst;
        wcex.lpszClassName = lpszClass;

        if (!RegisterClassEx(&wcex)) return false;
    }

    return true;
}

void Window::GetWndClassEx( WNDCLASSEX & wc )
{
    memset(&wc, sizeof(WNDCLASSEX), 0);
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = GetModuleHandle(NULL);
    wc.hIcon = LoadIcon(instance_handle, MAKEINTRESOURCE(IDI_ICON));
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW);
    wc.lpszClassName = NULL;
    wc.lpszMenuName = NULL;
    wc.hIconSm = LoadIcon(instance_handle, MAKEINTRESOURCE(IDI_ICON));
}

void Window::ShowWindow( BOOL show ) const
{
    if (show) {
        ::ShowWindow(window_handle, SW_SHOW);
        ::UpdateWindow(window_handle);
    }
    else {
        ::ShowWindow(window_handle, SW_HIDE);
    }
}

void Window::Destroy( void )
{
    if (window_handle) {
        DestroyWindow(window_handle);
        window_handle = NULL;
    }
}

int Window::MessageLoop( void )
{
    MSG msg;
    //HACCEL hAccelTable = LoadAccelerators(
    //    GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_ACCELERATOR));

    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!IsDialogMessage(msg.hwnd, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}

LRESULT CALLBACK Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    Window *pWindow;

    if (message == WM_NCCREATE)
    {
        assert(!IsBadReadPtr((LPVOID)lParam, sizeof(CREATESTRUCT)));
        pWindow = (Window*)((CREATESTRUCT*)lParam)->lpCreateParams;
        SetWindowLong(hWnd, GWL_USERDATA, (LONG)pWindow);
    }
    else
    {
        pWindow = (Window*)GetWindowLong(hWnd, GWL_USERDATA);
    }

    if (pWindow)
        return pWindow->WndProc(hWnd, message, wParam, lParam);
    else
        return DefWindowProc(hWnd, message, wParam, lParam);
}

void Window::OnCreate( HWND hWnd, LPCREATESTRUCT lpCreateStruct )
{

}

void Window::OnNotify( int idCtrl, LPNMHDR pnmh )
{

}

void Window::OnCommand( int wmId, int wmEvent, LPARAM lParam )
{

}

void Window::OnPaint()
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(window_handle, &ps);

    // TODO: OnPaint()

    EndPaint(window_handle, &ps);

}

void Window::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{

}

void Window::OnKeyUp( UINT nChar, UINT nRepCnt, UINT nFlags )
{

}

void Window::OnLButtonDown( UINT nFlags, Point point )
{

}

void Window::OnMouseMove( UINT nFlags, Point point )
{

}
